﻿using Dapper.Contrib.Extensions;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;

namespace Dapper.Core
{
    /// <summary>
    /// 非泛型基础仓储
    /// </summary>
    public partial class BaseRepository : IBaseRepository
    {
        /// <summary>
        /// 数据库类型
        /// </summary>
        public readonly DatabaseType _dbType;

        /// <summary>
        /// 数据库连接对象
        /// </summary>
        private readonly IDbConnection _dbConn;

        /// <summary>
        /// 依赖注入-获取工作单元对象
        /// </summary>
        /// <param name="unitOfWork"></param>
        public BaseRepository(IUnitOfWork unitOfWork)
        {
            _dbType = unitOfWork.DbType;
            _dbConn = unitOfWork.Connection;
        }

        /// <summary>
        /// 数据库连接对象
        /// </summary>
        public virtual IDbConnection Context
        {
            get
            {
                return _dbConn;
            }
        }

        #region 基础同步方法

        /// <summary>
        /// 同步查询返回动态类型
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <param name="commandType"></param>
        /// <returns></returns>
        public virtual T QueryFirstOrDefault<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
        {
            return Context.QueryFirstOrDefault<T>(sql, param, transaction, commandTimeout, commandType);
        }

        /// <summary>
        /// 同步查询返回特定类型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <param name="transaction"></param>
        /// <param name="buffered"></param>
        /// <param name="commandTimeout"></param>
        /// <param name="commandType"></param>
        /// <returns></returns>
        public virtual IEnumerable<T> Query<T>(string sql, object param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
        {
            return Context.Query<T>(sql, param, transaction, buffered, commandTimeout, commandType);
        }

        /// <summary>
        /// 同步执行命令
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <param name="commandType"></param>
        /// <returns></returns>
        public virtual int Execute(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
        {
            return Context.Execute(sql, param, transaction, commandTimeout, commandType);
        }

        /// <summary>
        /// 同步分页查询返回分页后的泛型集合
        /// </summary>
        /// <typeparam name="T">分页后的泛型集合</typeparam>
        /// <param name="sql">查询Sql语句或存储过程名称</param>
        /// <param name="param">参数值（可选）</param>
        /// <param name="transaction">事务名称（可选）</param>
        /// <param name="commandTimeout">超时时间（可选）</param>
        /// <param name="commandType">指定如果解释sql字符串：语句/存储过程（可选）</param>
        /// <returns>返回分页后的泛型集合</returns>
        public virtual Tuple<IEnumerable<T>, int> QueryPage<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
        {
            var multi = Context.QueryMultiple(sql, param, transaction, commandTimeout, commandType);
            int totalCount = int.Parse(multi.Read<long>().Single().ToString());
            return Tuple.Create<IEnumerable<T>, int>(multi.Read<T>(), totalCount);
        }

        #endregion

        #region 基础异步方法

        /// <summary>
        /// 同步查询返回动态类型
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <param name="transaction"></param>
        /// <param name="buffered"></param>
        /// <param name="commandTimeout"></param>
        /// <param name="commandType"></param>
        /// <returns></returns>
        public virtual async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
        {
            return await Context.QueryFirstOrDefaultAsync<T>(sql, param, transaction, commandTimeout, commandType);
        }

        /// <summary>
        /// 异步查询返回特定类型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <param name="commandType"></param>
        /// <returns></returns>
        public virtual async Task<IEnumerable<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
        {
            return await Context.QueryAsync<T>(sql: sql, param, transaction, commandTimeout, commandType);
        }

        /// <summary>
        /// 异步执行命令
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <param name="commandType"></param>
        /// <returns></returns>
        public virtual async Task<int> ExecuteAsync(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
        {
            return await Context.ExecuteAsync(sql, param, transaction, commandTimeout, commandType);
        }

        /// <summary>
        /// 异步分页查询返回分页后的泛型集合
        /// </summary>
        /// <typeparam name="T">分页后的泛型集合</typeparam>
        /// <param name="sql">查询Sql语句或存储过程名称</param>
        /// <param name="param">参数值（可选）</param>
        /// <param name="transaction">事务名称（可选）</param>
        /// <param name="commandTimeout">超时时间（可选）</param>
        /// <param name="commandType">指定如果解释sql字符串：语句/存储过程（可选）</param>
        /// <returns>返回分页后的泛型集合</returns>
        public virtual async Task<Tuple<IEnumerable<T>, int>> QueryPageAsync<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
        {
            var multi = await Context.QueryMultipleAsync(sql, param, transaction, commandTimeout, commandType);
            int totalCount = int.Parse(multi.Read<long>().Single().ToString());
            return Tuple.Create<IEnumerable<T>, int>(multi.Read<T>(), totalCount);
        }

        #endregion      
    }

    /// <summary>
    /// 泛型基础仓储实现类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public partial class BaseRepository<T> : BaseRepository, IBaseRepository<T>
        where T : class, new()
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="serviceProvider"></param>
        /// <param name="db"></param>
        public BaseRepository(IUnitOfWork unitOfWork) : base(unitOfWork)
        {
        }

        #region 扩展同步方法
        /// <summary>
        /// 同步获取一条
        /// </summary>
        /// <param name="id"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public virtual T Get(object id, IDbTransaction transaction = null, int? commandTimeout = null)
        {
            return Context.Get<T>(id, transaction, commandTimeout);
        }
        /// <summary>
        /// 同步获取所有实体
        /// </summary>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public virtual IEnumerable<T> GetAll(IDbTransaction transaction = null, int? commandTimeout = null)
        {
            return Context.GetAll<T>(transaction, commandTimeout);
        }

        /// <summary>
        /// 同步新增一条
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public virtual long Insert(T entity, IDbTransaction transaction = null, int? commandTimeout = null)
        {
            return Context.Insert(entity, transaction, commandTimeout);
        }

        /// <summary>
        /// 同步新增多条
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public virtual long Insert(IEnumerable<T> entities, IDbTransaction transaction = null, int? commandTimeout = null)
        {
            return Context.Insert(entities, transaction, commandTimeout);
        }

        /// <summary>
        /// 同步更新一条
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public virtual bool Update(T entity, IDbTransaction transaction = null, int? commandTimeout = null)
        {
            return Context.Update(entity, transaction, commandTimeout);
        }

        /// <summary>
        /// 同步更新多条
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public virtual bool Update(IEnumerable<T> entities, IDbTransaction transaction = null, int? commandTimeout = null)
        {
            return Context.Update(entities, transaction, commandTimeout);
        }

        /// <summary>
        /// 同步删除一条
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public virtual bool Delete(T entity, IDbTransaction transaction = null, int? commandTimeout = null)
        {
            return Context.Delete(entity, transaction, commandTimeout);
        }

        /// <summary>
        /// 同步删除多条
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public virtual bool Delete(IEnumerable<T> entities, IDbTransaction transaction = null, int? commandTimeout = null)
        {
            return Context.Delete(entities, transaction, commandTimeout);
        }
        #endregion

        #region 扩展异步方法
        /// <summary>
        /// 异步获取一条
        /// </summary>
        /// <param name="id"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public virtual async Task<T> GetAsync(object id, IDbTransaction transaction = null, int? commandTimeout = null)
        {
            return await Context.GetAsync<T>(id, transaction, commandTimeout);
        }

        /// <summary>
        /// 异步获取所有实体
        /// </summary>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public virtual async Task<IEnumerable<T>> GetAllAsync(IDbTransaction transaction = null, int? commandTimeout = null)
        {
            return await Context.GetAllAsync<T>(transaction, commandTimeout);
        }

        /// <summary>
        /// 异步新增一条
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <param name="sqlAdapter"></param>
        /// <returns></returns>
        public virtual async Task<int> InsertAsync(T entity, IDbTransaction transaction = null, int? commandTimeout = null, ISqlAdapter sqlAdapter = null)
        {
            return await Context.InsertAsync(entity, transaction, commandTimeout, sqlAdapter);
        }

        /// <summary>
        /// 异步新增多条
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <param name="sqlAdapter"></param>
        /// <returns></returns>
        public virtual async Task<int> InsertAsync(IEnumerable<T> entities, IDbTransaction transaction = null, int? commandTimeout = null, ISqlAdapter sqlAdapter = null)
        {
            return await Context.InsertAsync(entities, transaction, commandTimeout, sqlAdapter);
        }

        /// <summary>
        /// 异步更新一条
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public virtual async Task<bool> UpdateAsync(T entity, IDbTransaction transaction = null, int? commandTimeout = null)
        {
            return await Context.UpdateAsync(entity, transaction, commandTimeout);
        }

        /// <summary>
        /// 异步更新多条
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public virtual async Task<bool> UpdateAsync(IEnumerable<T> entities, IDbTransaction transaction = null, int? commandTimeout = null)
        {
            return await Context.UpdateAsync(entities, transaction, commandTimeout);
        }

        /// <summary>
        /// 异步删除一条
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public virtual async Task<bool> DeleteAsync(T entity, IDbTransaction transaction = null, int? commandTimeout = null)
        {
            return await Context.DeleteAsync(entity, transaction, commandTimeout);
        }

        /// <summary>
        /// 异步删除多条
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <returns></returns>
        public virtual async Task<bool> DeleteAsync(IEnumerable<T> entities, IDbTransaction transaction = null, int? commandTimeout = null)
        {
            return await Context.DeleteAsync(entities, transaction, commandTimeout);
        }

        #endregion

        #region 扩展分页方法

        /// <summary>
        /// 同步根据条件查询数据库,并返回对象集合(用于分页数据显示)
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <param name="fields">字段</param>
        /// <param name="condition">条件</param>
        /// <param name="info">分页实体</param>
        /// <param name="trans">事务对象</param>
        /// <param name="commandTimeout">超时时间</param>
        /// <returns>指定对象的集合</returns>
        public virtual List<T> GetPage(string tableName, string fields, string condition, PageParam info, IDbTransaction trans = null, int? commandTimeout = null)
        {
            List<T> list;

            if (string.IsNullOrEmpty(condition))
            {
                condition = "1=1";
            }

            PagerHelper pagerHelper = new(tableName, fields, condition, info.IsDesc, info.Sort, info.PageIndex, info.PageSize);
            string pageSql = pagerHelper.GetPagingSql(true, _dbType);
            pageSql += ";" + pagerHelper.GetPagingSql(false, _dbType);

            var reader = Context.QueryMultiple(pageSql, null, trans, commandTimeout);
            info.RecordCount = reader.ReadFirst<int>();
            list = reader.Read<T>().AsList();
            return list;
        }

        /// <summary>
        /// 异步根据条件查询数据库,并返回对象集合(用于分页数据显示)
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <param name="fields">字段</param>
        /// <param name="condition">条件</param>
        /// <param name="info">分页实体</param>
        /// <param name="trans">事务对象</param>
        /// <param name="commandTimeout">超时时间</param>
        /// <returns>指定对象的集合</returns>
        public virtual async Task<List<T>> GetPageAsync(string tableName, string fields, string condition, PageParam info, IDbTransaction trans = null, int? commandTimeout = null)
        {
            List<T> list;

            if (string.IsNullOrEmpty(condition))
            {
                condition = "1=1";
            }

            PagerHelper pagerHelper = new(tableName, fields, condition, info.IsDesc, info.Sort, info.PageIndex, info.PageSize);
            string pageSql = pagerHelper.GetPagingSql(true, _dbType);
            pageSql += ";" + pagerHelper.GetPagingSql(false, _dbType);

            var reader = await Context.QueryMultipleAsync(pageSql, null, trans, commandTimeout);
            info.RecordCount = reader.ReadFirst<int>();
            list = reader.Read<T>().AsList();
            return list;
        }

        #endregion
    }
}